package com.core;
import java.util.*;


public abstract class AbstractConstant implements Constant {

    /**
     * HashMap containing all possible values for each constant class.
     */
    private static HashMap values = new HashMap();

    /**
     * The value of this constant. Used for looking up a constant by supplying its value.
     */
    protected String value;

    /**
     * Default constructor. Not to be used.
     */
    public AbstractConstant() { }

    /**
     * Constructs a new Constant with a specific name. This constant is added
     * to the list of constants. Since all constants are static final
     * the constructor is called when each constant class is used the first time.
     *
     * @param value  of the constant
     */
    protected AbstractConstant(String value) {
        this.value = value;
        ArrayList list = (ArrayList) values.get(this.getClass());
        if (list == null) {
            list = new ArrayList();
            values.put(this.getClass(), list);
        }
        list.add(this);
    }

    /**
     * Get all constants for the class. The class must be a subclass of Constant.
     * Null is returned if the class contains no constants or if it is no subclass
     * to constant.
     *
     * @param clazz  the constant clazz for which all constants should be returned
     * @return       a collection of all constants for the constant class
     */
    public static Collection getAllConstants(Class clazz) {
        return (Collection) values.get(clazz);
    }

    /**
     * Get the constant from the name. This is typically used when fetching data from a database or other external system.
     * If there is no constant with this value of this Constant class then null is returned.
     *
     * @param clazz  subclass of Constant
     * @param value  of the constant
     * @return       the constant or null if no constant is defined
     */
    protected static AbstractConstant getConstant(Class clazz, String value) {
        ArrayList list = (ArrayList) values.get(clazz);
        Iterator it = list.iterator();
        while (it.hasNext()) {
            AbstractConstant constant = (AbstractConstant) it.next();
            // Note that if we below use constant.getValue.equals(), we will create an endless loop
            if (constant.value != null && value != null && constant.value.equalsIgnoreCase(value.trim())) {
           		return constant;
            } else if (constant.value == null && value == null) {
            	return constant;
            } 
        }
        return null;
    }

    /**
     * Gets the actual value of the constant.
     *
     * @return   the value of the constant
     */
    public String getValue() {
        return value;
    }

    /**
     * Creates a string representation of the constant value.
     *
     * @return   the value as a string
     */
    public String toString() {
        return value;
    }

    /**
     * Compares two Constant objects for equality. If the object
     * passed to this method is of type String then a conversion of the String
     * value into a constant of the same type as the current constant is done.
     * If it succeedes then a comparision between the two constants is done.
     *
     * @param obj  the object to compare
     * @return     true if equal, false otherwise
     */
    public boolean equals(Object obj) {

        boolean equality = false;

        if (obj instanceof Constant) {
            Constant constant = (Constant) obj;
            if (getValue() != null) {
                equality = getValue().equals(constant.getValue());
            } else {
                equality = constant.getValue() == null;
            }
        } else if (obj instanceof String) {
            Constant constant = getConstant(this.getClass(), (String) obj);
            if (constant != null) {
                if (getValue() != null) {
                    equality = getValue().equals(constant.getValue());
                } else {
                    equality = constant.getValue() == null;
                }
            }
        }

        return equality;
    }

    /**
     * Calculates the hash code for this constant by returning the hash code for its value
     *
     * @return   the hash code
     */
    public int hashCode() {
        return this.value.hashCode();
    }

    /**
     * Compares to Constants on the toString method.
     *
     * @param obj  other Constants to compare with
     * @return     negative integer, zero or positive integer if the supplied object is less than, equal or greater than this Constant
     */
    public int compareTo(Object obj) {
        int result = 1;
        //Default to be greater than
        if (toString() != null && obj != null) {
            //Compare on toString to allow subclasses to override the toString method
            result = toString().compareTo(obj.toString());
        }
        return result;
    }

}